module aru_psb_rdgen_conv (
    input logic                   clk,
    input logic                   rst_n,
          aru_psb_dat_fp32_if.in  psb_in,  // input interface: .vld, .dat (32), .rdy
          aru_psb_dat_bf16_if.out psb_out  // output interface: .vld, .dat (16), .rdy
);

    localparam int LEN = `P_ARU * `N0;

    // single-entry pipeline registers
    logic     in_valid_d;
    psb_dat_t in_payload_d;


    // combinational converted output for whole array
    aru_dat_t out_payload_comb;

    // output valid
    logic     out_vld_comb;

    // round-to-nearest-even implemented via roundBit, sticky, lsb approach.
    function automatic bf16_t fp32_to_bf16_comb(input fp32_t x);
        logic        s;
        logic [ 7:0] e;
        logic [22:0] f;
        logic [22:0] sfrac;
        logic expZero, expInf, fracZero, reprSubNormal;
        logic resultLSB, roundBit, sticky;
        logic  [6:0] frac_ret;
        logic        round;
        logic  [6:0] fracR;
        logic  [7:0] expR;
        bf16_t       out;
        begin
            s             = x[31];
            e             = x[30:23];
            f             = x[22:0];

            expZero       = (e == 8'd0);
            expInf        = (e == 8'hFF);
            fracZero      = (f == 23'd0);
            reprSubNormal = f[22];

            if (expZero && reprSubNormal) begin
                sfrac = {f[21:0], 1'b0};
            end else begin
                sfrac = f;
            end

            resultLSB = sfrac[16];
            roundBit  = sfrac[15];
            sticky    = (|sfrac[14:0]);

            frac_ret  = sfrac[22:16];
            round     = roundBit && (sticky || resultLSB);

            if (expInf && fracZero) begin
                expR  = 8'hFF;
                fracR = 7'd0;
            end else if (expInf && !fracZero) begin
                expR  = 8'hFF;
                fracR = 7'b1000000;  // qNaN canonical
            end else if (expZero && fracZero) begin
                expR  = 8'd0;
                fracR = 7'd0;
            end else begin
                expR  = e;
                fracR = frac_ret;
                if (round) begin
                    logic [7:0] sum8;
                    sum8 = {1'b0, fracR} + 8'd1;
                    if (sum8[7]) begin
                        fracR = sum8[6:0];
                        expR  = expR + 8'd1;
                    end else begin
                        fracR = sum8[6:0];
                    end
                end
                if (expR == 8'hFF) begin
                    fracR = 7'd0;
                end
            end

            out = {s, expR, fracR};
            return out;
        end
    endfunction

    integer i;
    always_comb begin
        logic [31:0] temp;
        // default
        for (i = 0; i < LEN; i = i + 1) begin
            out_payload_comb.dat[i] = 16'h0;
        end
        out_vld_comb = 1'b0;
        if (in_valid_d) begin
            out_vld_comb = 1'b1;
            for (i = 0; i < LEN; i = i + 1) begin
                temp                    = in_payload_d.dat[i];
                out_payload_comb.dat[i] = fp32_to_bf16_comb(temp);
            end
        end
    end

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            in_valid_d   <= 1'b0;
            in_payload_d <= '0;
        end else begin
            if (psb_in.vld && psb_in.rdy) begin
                in_valid_d   <= 1'b1;
                in_payload_d <= psb_in.dat;
            end else if (psb_out.vld && psb_out.rdy) begin
                in_valid_d <= 1'b0;
            end
        end
    end

    assign psb_out.dat = out_payload_comb;
    assign psb_out.vld = out_vld_comb;

    // input ready: accept new if slot free OR we will consume this cycle
    assign psb_in.rdy  = (~in_valid_d) || (psb_out.vld && psb_out.rdy);

endmodule
